/* vcc/plugins/vcr/vcr.c 
 * 
 * This file is part of vcc. 
 * 
 * vcc is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * vcc is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with vcc. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>

#include <vcc/vcc.h>
#include <vcc/pretty.h>
#include <vcc/plugin.h>
#include <vcc/hooks.h>
#include <vcc/interfaces.h>
#include <vcc/version.h>

#include <readconf/readconf.h>
#include <readconf/do_op_set.h>


extern char 			**environ;
static int 			vcr_listen_sid;
static char 			*vcr_executable;
extern char 			usrname[];

static int vcr_init(void);

struct cmdtable vcr_cmdtab[] = {
	{ NULL, NULL, NULL }
};


struct plugin plg_vcr = {
	.state = 0, 
	.name = "vcr", 
	.descr = "vcc bridge. ", 
	.cmdtab = vcr_cmdtab, 
	.init = vcr_init
};

#define this_plg (&plg_vcr)

static char *set_environments(struct hook_info *hi) {
	char *t;

	if (unlikely(!(t = malloc(64)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return NULL;
	}

	sprintf(t, "%d", hi->sid);

	setenv("VCR_TYPE", hi->type == HOOK_RECV ? "recv" : "send", 1);
	setenv("VCR_USER", hi->usr, 1);
	setenv("VCR_MSG", hi->msg, 1);
	setenv("VCR_SID", t, 1);

	return t;
}

static int unset_environments(void) {
	unsetenv("VCR_TYPE");
	unsetenv("VCR_USER");
	unsetenv("VCR_MSG");
	unsetenv("VCR_SID");

	return 0;
}

static int send_result(int fd) {
	char p[MSG_SIZE];

	read(fd, &p, MSG_SIZE);
	p[MSG_SIZE - 1] = 0;

	send_msg(p, usrname);
	return 0;
}

static int vcr_execute(struct hook_info *hi) {
	int pid, status;
	int pipefd[2];
	char *t;

	t = set_environments(hi);
	if (pipe(pipefd) < 0) {
		perror("pipe");
		return 1;
	}

	switch ((pid = fork())) {
	case 0:
		close(pipefd[0]);
		dup2(pipefd[1], 1);

		execlp(vcr_executable, vcr_executable, NULL);
		printf("%s\n", vcr_executable);
		perror("execlp");

		abort();

	case -1:
		perror("fork");
		return 1;

	default:
		wait4(pid, &status, 0, NULL);
		printf("%d exited with status %d. \n", pid, status);

		send_result(pipefd[0]);

		unset_environments();
		free(t);
	}

	close(pipefd[0]);
	close(pipefd[1]);

	return status;
}

static char *vcr_hook(struct hook_info *hi) {
	if (hi->sid != vcr_listen_sid) 
		return HOOK_IGNORE;

	vcr_execute(hi);

	return HOOK_IGNORE;
}

static int load_vcr_config(void) {
	struct readconf_uintconf *conf_sid;
	struct readconf_strconf *conf_exec;

	if (!(conf_sid = readconf_find_uintname("vcr_listen_sid")) || 
			!(conf_exec = readconf_find_strname("vcr_executable"))) {
		fprintf(stderr, "*** vcr: cannot load configure\n");
		return 1;
	}

	vcr_listen_sid = (int) conf_sid->conf_value;
	vcr_executable = conf_exec->conf_value;

	printf("vcr is listening on sid %d. \n", vcr_listen_sid);
	printf("vcr's executable is %s\n", vcr_executable);

	return 0;
}

static int vcr_init(void) {
	if (load_vcr_config()) 
		return 1;

	if (mode != MODE_ROBOT) {
		fprintf(stderr, "*** vcr should be loaded in the robot mode. \n");
		return 1;
	}

	register_hook(HOOK_SEND, vcr_hook);
	register_hook(HOOK_RECV, vcr_hook);

	join_session((current_sid = vcr_listen_sid));

	return 0;
}


